using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using EnvDTE;

namespace Behaviorize.CodeGenerator.CodeModel
{
	public class StepDefinition
	{
		private readonly string _text;
		private readonly Scenario _scenario;

		public StepDefinition(string text, Scenario scenario)
		{
			_text = text;
			_scenario = scenario;
		}

		public List<StepMethodParameter> GetMethodParameters(bool useAnonymousParameterNames)
		{
			List<StepMethodParameter> parameters = new List<StepMethodParameter>();
			Regex regex = new Regex(@"\<(?<paramname>[\w\d]+)\>");
			MatchCollection parameterMatches = regex.Matches(_text);

			string[] anonymousParameterNames = new[] { "x", "y", "z", "a", "b", "c", "m", "n", "o", "p", "q", "r", "s", "t" };
			int paramCounter = 0;

			foreach (Match parameterMatch in parameterMatches)
			{
				string paramName = parameterMatch.Groups["paramname"].Value;
				Type type = _scenario.Examples[paramName].First().GetType();
				parameters.Add(new StepMethodParameter((useAnonymousParameterNames ? anonymousParameterNames[paramCounter++] : paramName), type.AsVsCMTypeRef()));
			}

			return parameters;
		}

		private IEnumerable<string> GetMethodParameterNames()
		{
			Regex regex = new Regex(@"\<(?<paramname>[\w\d]+)\>");
			MatchCollection parameterMatches = regex.Matches(_text);

			foreach (Match parameterMatch in parameterMatches)
			{
				string paramName = parameterMatch.Groups["paramname"].Value;
				yield return paramName;
			}
		}

		public List<vsCMTypeRef> GetMethodParameterTypes()
		{
			List<vsCMTypeRef> parameters = new List<vsCMTypeRef>();
			Regex regex = new Regex(@"\<(?<paramname>[\w\d]+)\>");
			MatchCollection parameterMatches = regex.Matches(_text);

			foreach (Match parameterMatch in parameterMatches)
			{
				string paramName = parameterMatch.Groups["paramname"].Value;
				var type = _scenario.Examples[paramName].First().GetType().AsVsCMTypeRef();
				parameters.Add(type);
			}

			return parameters;
		}

		public string GetMethodName()
		{
			Regex regex = new Regex(@"\<[\w\d]+\>");
			return regex.Replace(_text.AsPascal(), "_");
		}

		public override string ToString()
		{
			return _text;
		}

		public string GetInvokationExpression(Dictionary<string, object> exampleRow)
		{
			string expression = GetMethodName();
			IEnumerable<string> methodParameters = GetMethodParameterNames();
			
			foreach (var methodParameter in methodParameters)
			{
				expression += ", " + EncloseValue(exampleRow[methodParameter]);
			}

			return expression;
		}

		private static string EncloseValue(object value)
		{
			return value is string ? "\"" + value + "\"" : value.ToString();
		}

		public int GetMethodSignatureHashCode()
		{
			unchecked
			{
				int hash = 17 + GetMethodName().GetHashCode();

				foreach (vsCMTypeRef parameterType in GetMethodParameterTypes())
				{
					hash = hash * 23 + parameterType.GetHashCode();
				}

				return hash;
			}
		}

		public bool ParametersMatch(CodeElements parameters)
		{
			List<StepMethodParameter> correctParameters = GetMethodParameters(false);

			if (parameters.Count != correctParameters.Count) return false;

			int i = 0;
			foreach (CodeParameter parameter in parameters)
			{
				StepMethodParameter correctParameter = correctParameters[i];

				if (parameter.Name != correctParameter.Name || parameter.Type.TypeKind != correctParameter.Type)
				{
					return false;
				}

				i++;
			}

			return true;
		}
	}

	public class StepMethodParameter
	{
		public string Name { get; set; }
		public vsCMTypeRef Type { get; set; }

		public StepMethodParameter(string name, vsCMTypeRef type)
		{
			Name = name;
			Type = type;
		}
	}
}